package com.atguigu.gmall.product.client.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.product.client.common.result.Result;
import com.atguigu.gmall.product.client.common.result.ResultCodeEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

//全局过滤器  校验是否登录  默认9大过滤器  添加这个方法是10个方法

@Component  //实例化注解
public class GlobalLoginFilter implements GlobalFilter, Ordered {
    //配置URI路径的类  可以使用正则表达式使用
    private AntPathMatcher antPathMatcher= new AntPathMatcher();
    //浏览器中令牌的别称
    public static final String TOKEN ="token";
    //浏览器中临时用户id
    public static final String USERTEMPID = "userTempId";
    //RedisConfig配置类 创建RedisTemplate  不再是原生redis客户端
    @Autowired
    private RedisTemplate redisTemplate;  //格式有问题  K类型与V类型  原生
    @Value("${auth.url}")
    private String[] authUrl;
    public static final String LOGINURL = "http://passport.gmall.com/login.html?originUrl=";

        //全局过滤器方法
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //url :获取到的全路径
        //uri :获取到的接口
        //path :

        //1. 获取当前uri
        String path = request.getURI().getPath();

        //2. 内部资源不允许浏览器访问  /**/inner/**  登录与未登录都不行
        if (antPathMatcher.match("/**/inner/**",path)){
            //是内部资源 无权访问
            return getVoidMono(response,ResultCodeEnum.PERMISSION);
        }

        String userId = getUserId(request);
        //3. 判断当前URI是否为必须登录  /auth  开始就要登录了 否则不用登录
        //3.1 异步   /**/auth/** 网关直接连接redis判断到底登录了还是没有登录 未登录直接返回值201码  如果登录了直接放行
        if (antPathMatcher.match("/**/auth/**",path)){
            //当前path是需要登录  判断用户是否登录
            if (StringUtils.isEmpty(userId)){
                //未登录
                return getVoidMono(response,ResultCodeEnum.LOGIN_AUTH);
            }
        }
        // 异步: 不会改变原地址路径   同步: 会改变原地址路径
        //3.2 同步  网关路由到后面页面微服务
        //加入购物车  1直接弹窗  2转发到新页面
        for (String url : authUrl) {
            //url.indexOf(path) != -1 是按照路径的索引进行判断的  路径写在配置文件
            if (url.indexOf(path) != -1 && StringUtils.isEmpty(userId)){
                //需要登录 在判断是否需要登录 未登录
                //返回的状态码
                response.setStatusCode(HttpStatus.SEE_OTHER); //303重定向
                //重定向到登录的页面
                try {
                    response.getHeaders().set(HttpHeaders.LOCATION,
                            LOGINURL + URLEncoder.encode(request.getURI().toString(),"utf-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return response.setComplete(); //完成之后就可以响应了
            }
        }
        //加入购物车
        //当前是网关    传递用户id给后面的微服务   请求头: k:userId,v:"3"
        //引用了之前的请求头携带参数的对象 在创建一个新的请求头并将参数设置进去
        // request(旧)掉exchange  创建新request request(新)掉exchange
        //  1. 传递真实用户id
        if (!StringUtils.isEmpty(userId)){
            request.mutate().header("userId",userId);
        }
        //  2. 传递临时用户id
        String userTempId = getUserTempId(request);
        if (!StringUtils.isEmpty(userTempId)){
            request.mutate().header("userTempId",userTempId);
        }


        //放行
        return chain.filter(exchange);
    }

    //过滤器执行的顺序  负整数最大值    0   正整数最大值
                   //最优先执行    中   最后执行
    @Override
    public int getOrder() {
        return 0;
    }

        //判断用户是否登录 获取用户ID
    private String getUserId(ServerHttpRequest request) {
        //1. 获取令牌
        String token = request.getHeaders().getFirst(TOKEN);   //在请求头获取token
        if (StringUtils.isEmpty(token)){  //判断令牌在请求头中有没有
            //如果没有 查看令牌是否在cookie中
            HttpCookie httpCookie = request.getCookies().getFirst(TOKEN);
            if (httpCookie != null){   //判断令牌在cookie中的值
                token = httpCookie.getValue();   //返回给token
            }
        }

        //2. 有 去缓存中查询是否存在用户Id   存在:已登录  不存在:未登录
        if (!StringUtils.isEmpty(token)){   //如果token不为空
            if (redisTemplate.hasKey(token)){  //判断是否存在用户Id   如果不存在 就是未登录 直接走3
                return (String) redisTemplate.opsForValue().get(token);
            }
        }

        //3. 没有 未登录
        return null;
    }


    private Mono<Void> getVoidMono(ServerHttpResponse response,ResultCodeEnum resultCodeEnum) {
        Result<Object> r = Result.build(null, resultCodeEnum);//无权访问
        String result = JSONObject.toJSONString(r);
        DataBuffer dataBuffer = response.bufferFactory().wrap(result.getBytes());  //Result对象转换为Mono对象  使用数据流

        //解决response乱码问题
        //HttpHeaders为请求头的枚举  CONTENT_TYPE常量 ,  utf-8
        response.getHeaders().set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_PROBLEM_JSON_UTF8_VALUE);
        return response.writeWith(Mono.just(dataBuffer));
    }

    private String getUserTempId(ServerHttpRequest request) {
        //请求头
        String userTempId = request.getHeaders().getFirst(USERTEMPID);   //在请求头获取token
        if (StringUtils.isEmpty(userTempId)){  //判断令牌在请求头中有没有
            //如果没有 查看令牌是否在cookie中
            HttpCookie httpCookie = request.getCookies().getFirst(USERTEMPID);
            if (httpCookie != null){   //判断令牌在cookie中的值
                userTempId = httpCookie.getValue();   //返回给token
            }
        }
        return userTempId;
    }

}
